package OpenID::Tester::Test::Discovery;
use strict;
use base 'OpenID::Tester::Test';

sub needs_op { 1 }
sub needs_rp { 1 }

sub needed_op_caps { () }
sub needed_rp_caps {
    my $self = shift;
    return $self->_caps_for_version($self->{rp_version})
}

sub tests {
    my ($class, $tester) = @_;
    my @tests;
    my @versions = ("1.1", "2.0", "1.1,2.0");
    foreach my $rp_version (@versions) {
        foreach my $markup_version (@versions) {
            foreach my $delegate_version (undef, @versions) {
                foreach my $redirect (0, 1) {
                    push @tests, $class->new(
                        $tester,
                        rp_version => $rp_version,
                        markup_version => $markup_version,
                        delegate_version => $delegate_version,
                        redirect => $redirect,
                    );
                }
            }
        }
    }
    return @tests;
}

sub init {
    my ($self, %args) = @_;
    $self->{rp_version} = $args{rp_version};
    $self->{markup_version} = $args{markup_version};
    $self->{delegate_version} = $args{delegate_version};
    $self->{redirect} = $args{redirect};
}

sub des {
    "Tests all of the permutations of discovery";
}

sub summary {
    my $self = shift;
    my $ret = $self->{rp_version}." RP processing " . $self->{markup_version}." markup";
    $ret .= " delegating to ".$self->{delegate_version}." endpoint" if $self->{delegate_version};
    $ret .= " with a redirect" if $self->{redirect};
    return $ret;
}

sub run {
    my $self = shift;
    my $op_base = $self->tester->op_base;
    my $rp_base = $self->tester->rp_base;

    # $entered_identity_url is what we imagine the user entered into the login field
    # $resulting_identity_url is the URL that should be returned from discovery
    # $delegate_identity_url is what we should find as the delegate if we're doing delegation

    my $resulting_identity_url = $op_base . "/$self->{markup_version}/identity/will-sign";
    my $delegate_identity_url = $self->{delegate_version} ? $resulting_identity_url : '';
    $resulting_identity_url .= "/delegate_to/$self->{delegate_version}" if $self->{delegate_version};

    my $entered_identity_url = $resulting_identity_url;
    $entered_identity_url .= "/redirect" if $self->{redirect};

    # Discovery should fail if the RP and the identity don't support the same version(s).
    my $should_fail = $self->{markup_version} ne $self->{rp_version} ? 1 : 0;

    # Also check that we end up with the right provider endpoint URL.
    # This will be the endpoint for the highest version supported by both
    # the RP and the identity URL.
    my $provider_endpoint_url;
    if ($self->{delegate_version}) {
        $provider_endpoint_url = $op_base."/".$self->{delegate_version}."/op";
    }
    else {
        if ($self->{rp_version} =~ /\b2\.0\b/ && $self->{markup_version} =~ /\b2\.0\b/) {
            $provider_endpoint_url = $op_base."/2.0/op";
        }
        else {
            $provider_endpoint_url = $op_base."/1.1/op";
        }
    }

    my $check_url = $self->gen_url(
        $rp_base . "/$self->{rp_version}/rp",
        openid_identifier => $entered_identity_url,
        op => 'disco',
    );
    $self->status("Checking URL: $check_url");
    my $ua = $self->tester->ua;
    my $res = $ua->get($check_url);
    die "Response not successful: " . $res->status_line unless $res->is_success;

    my $content = $res->content;
    my %result = ();
    foreach my $l (split(/\n/, $content)) {
        $l =~ s/#.*$//g;
        $l =~ s/\s*$//g;
        my ($k, $v) = split(/\s*:\s*/, $l, 2);
        $result{$k} = $v;
    }

    unless ($should_fail) {
        die "Discovery failed altogether " unless $result{openid_provider};
        die "Resulting identity URL is incorrect (got $result{user_specified_url} while expecting $resulting_identity_url)" unless $result{user_specified_url} eq $resulting_identity_url;
        die "Resulting delegate URL is incorrect (got $result{local_id} while expecting $delegate_identity_url)" unless $result{local_id} eq $delegate_identity_url;
        die "Resulting provider endpoint is incorrect (got $result{openid_provider} while expecting $provider_endpoint_url)" unless $result{openid_provider} eq $provider_endpoint_url;
    }
    else {
        die "Should have failed but did not" if $result{openid_provider};
    }
    return 1;
}

1;
